package trunk.control;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;


import java.util.ArrayList;

import trunk.res.R;

/**
 * central control and dispatch for each component
 * Created by IntelliJ IDEA.
 * User: yipeipei
 * Date: 12/24/11
 * Time: 7:34 PM
 * To change this template use File | Settings | File Templates.
 */
public class Control {
    /*Singleton*/
    int asd;
    private static Control control = new Control();
    private Method SimulatorPanel;
    private StringBuffer microcodes = new StringBuffer();
    private StringBuffer outputInstruction = new StringBuffer();

     public Timer timer = new java.util.Timer();
    
    
    private Control() {
    }

    public static Control getInstant() {
        return control;
    }

    /*Instructions parameters*/
    private int instructionType = -1;   //initialize to -1 for invalid
    private int instruction = -1;
    private int sourceOperandMod = -1;
    private int targetOperandMod = -1;

    /*Render parameters*/
    private int speed = 1;
    private boolean startFlag = false;
    private boolean pauseFlag = false;
    private boolean stepFlag = false;

    public int getInstructionType() {
        return instructionType;
    }

    public int getInstruction() {
        return instruction;
    }

    public int getSourceOperandMod() {
        return sourceOperandMod;
    }

    public int getTargetOperandMod() {
        return targetOperandMod;
    }

    public int getSpeed() {
        return speed;
    }

    public boolean isStartFlag() {
        return startFlag;
    }

    public boolean isPauseFlag() {
        return pauseFlag;
    }

    public boolean isStepFlag() {
        return stepFlag;
    }

    public void setInstructionType(int instructionType) {
        this.instructionType = instructionType;
//        debugPrint();
    }

    public void setInstruction(int instruction) {
        this.instruction = instruction;
//        debugPrint();
    }

    public void setSourceOperandMod(int sourceOperandMod) {
        this.sourceOperandMod = sourceOperandMod;
//        debugPrint();
    }

    public void setTargetOperandMod(int targetOperandMod) {
        this.targetOperandMod = targetOperandMod;
//        debugPrint();
    }

    public void setSpeed(int speed) {
        this.speed = speed;
//        debugPrint();
    }

    public void setStartFlag(boolean startFlag) {
       
        this.startFlag = startFlag;
        
        if(startFlag == true){
            this.timer = new Timer();
            clearIntruction();
//            try {
//                Thread.sleep(800);
//            } catch (InterruptedException ex) {
//                Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
//            }
            actionControl();
            Launcher.simulator.getOutputinstructionPanel().printoutputInstruction(outputInstruction.toString());
            Launcher.simulator.getMicrocodePanel().printInstruction(microcodes.toString());
        }else if(startFlag == false){
            this.timer.cancel();
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
            this.timer = new Timer();
            Launcher.simulator.getSimulatePanel().clearaction();
        }

        
//        debugPrint();
    }
    
   private void  waitTime(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        }
   }

   public void clearIntruction(){
       int n=outputInstruction.length();
       outputInstruction.delete(0, n);
       n = microcodes.length();
       microcodes.delete(0, n);
   }

    public void setPauseFlag(boolean pauseFlag) {
        this.pauseFlag = pauseFlag;
//        debugPrint();
    }

    public void setStepFlag(boolean stepFlag) {
        this.stepFlag = stepFlag;
//        debugPrint();
    }

    //according the method name to call the method
    private void doShow(String action) {
        try {
                Method method = Class.forName("trunk.gui.SimulatePanel").getMethod(action);
                method.invoke(Launcher.simulator.getSimulatePanel());
                Thread.sleep(10);
        } catch (InterruptedException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvocationTargetException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchMethodException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }
    private void notdoShow(String action) {
        try {
                Method method = Class.forName("trunk.gui.SimulatePanel").getMethod(action);
                method.invoke(Launcher.simulator.getSimulatePanel());
                Thread.sleep(10);
        } catch (InterruptedException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvocationTargetException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchMethodException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(Control.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }

    private void execute(ArrayList<String> flow) {
        for(String microcode : flow){
            ArrayList<String> actionList = DataPacket.ACTION.get(microcode);
         
            for (String action : actionList) {
                doShow(action);
            }
          
            notdoShow("clearaction");
        }
    }
     
    public void actionControl() {
        
        ArrayList<String> flow = new ArrayList<String>();
        if (instructionType == 0) {
            outputInstruction.append(R.string.INSTRUCTION_TRANSFER[instruction]).append("  ");
            outputInstruction.append(R.string.SOURCE_OPERAND_ADDRESS_MODE[sourceOperandMod]).append(",");
            outputInstruction.append(R.string.TARGET_OPERAND_ADDRESS_MODE[targetOperandMod]).append("\n ");
            microcodes.append("FT:\n");
            for (String ft : DataPacket.FT) {
                  flow.add(ft);
                  microcodes.append("    ").append(ft).append("\n");
            }
            if(sourceOperandMod != 0) {
                microcodes.append("ST:\n");
                for (String st : DataPacket.ST.get(sourceOperandMod-1)) {
                  flow.add(st);
                  microcodes.append("    ").append(st).append("\n");
                }
            }
            if(targetOperandMod != 0){
                microcodes.append("DT:\n");
            if(targetOperandMod == 1){
                flow.add("R1->MAR");
                microcodes.append("    ").append("R1->MAR").append("\n");
            }else if(targetOperandMod == 2){
                flow.add("R1-1->R1,MAR");
                microcodes.append("    ").append("R1-1->R1,MAR").append("\n");
            }else if(targetOperandMod == 3){
                flow.add("R1->MAR");
                flow.add("R1+1->R1");
                microcodes.append("    ").append("R1->MAR").append("\n");
                microcodes.append("    ").append("R1+1->R1").append("\n");
            }else if(targetOperandMod == 4){
                flow.add("R1->MAR");
                flow.add("R1+1->R1");
                flow.add("M->MDR->MAR");
                microcodes.append("    ").append("R1->MAR").append("\n");
                microcodes.append("    ").append("R1+1->R1").append("\n");
                microcodes.append("    ").append("M->MDR->MAR").append("\n");
            }else if(targetOperandMod == 5){
                flow.add("PC->MAR");
                flow.add("M->MDR->D");
                flow.add("PC+1->PC");
                flow.add("D+R1->MAR");
                microcodes.append("    ").append("PC->MAR").append("\n");
                microcodes.append("    ").append("M->MDR->D").append("\n");
                microcodes.append("    ").append("PC+1->PC").append("\n");
                microcodes.append("    ").append("D+R1->MAR").append("\n");
            }
            }
            microcodes.append("ET:\n");
            if((sourceOperandMod == 0) && (targetOperandMod == 0)){
                flow.add("R0->R1");
                flow.add("PC->MAR");
                microcodes.append("    ").append("R0->R1").append("\n");
                microcodes.append("    ").append("PC->MAR").append("\n\n\n\n\n");
            }else if((sourceOperandMod == 0) && (targetOperandMod != 0)){
                flow.add("R0->MDR");
                flow.add("MDR->M");
                flow.add("PC->MAR");
                microcodes.append("    ").append("R0->MDR").append("\n");
                microcodes.append("    ").append("MDR->M").append("\n");
                microcodes.append("    ").append("PC->MAR").append("\n\n\n\n\n");
            }else if((sourceOperandMod != 0) && (targetOperandMod == 0)){
                flow.add("C->R1");
                flow.add("PC->MAR");
                 microcodes.append("    ").append("C->R1").append("\n");
                microcodes.append("    ").append("PC->MAR").append("\n\n\n\n\n");
            }else{
                flow.add("C->MDR");
                flow.add("MDR->M");
                flow.add("PC->MAR");
                microcodes.append("    ").append("C->MDR").append("\n");
                microcodes.append("    ").append("MDR->M").append("\n");
                microcodes.append("    ").append("PC->MAR").append("\n\n\n\n\n");
                
            }
            
        }else if (instructionType == 1) {
            
            if(instruction == 2){
            outputInstruction.append(R.string.INSTRUCTION_SINGLE[instruction]).append("  ");
            outputInstruction.append(R.string.TARGET_OPERAND_ADDRESS_MODE[targetOperandMod]).append("\n ");
                microcodes.append("FT:\n");
                for (String ft : DataPacket.FT) {
                        flow.add(ft);
                        microcodes.append("    ").append(ft).append("\n");
                    }
                
                if(targetOperandMod != 0) {
                    microcodes.append("DT:\n");
                    for (String dt : DataPacket.DT.get(targetOperandMod-1)) {
                        flow.add(dt);
                        microcodes.append("    ").append(dt).append("\n");
                }
            }
                microcodes.append("ET:\n");
                if(targetOperandMod == 0){
                    flow.add("R1+1->R1");
                    flow.add("PC->MAR");
                    microcodes.append("    ").append("R1+1->R1").append("\n");
                    microcodes.append("    ").append("PC->MAR").append("\n\n\n\n\n");
                }else{
                    flow.add("D+1->MDR");
                    flow.add("MDR->M");
                    flow.add("PC->MAR");
                    microcodes.append("    ").append("D+1->MDR").append("\n");
                    microcodes.append("    ").append("MDR->M").append("\n");
                    microcodes.append("    ").append("PC->MAR").append("\n\n\n\n\n");
                }
            }
        }else if (instructionType == 2) {
            
            if(instruction == 0){
                outputInstruction.append(R.string.INSTRUCTION_DOUBLE[instruction+1]).append("  ");
                outputInstruction.append(R.string.SOURCE_OPERAND_ADDRESS_MODE[sourceOperandMod+1]).append(",");
                outputInstruction.append(R.string.TARGET_OPERAND_ADDRESS_MODE[targetOperandMod+1]).append("\n ");
                microcodes.append("FT:\n");
                for (String ft : DataPacket.FT) {
                  flow.add(ft);
                  microcodes.append("    ").append(ft).append("\n");
                }
                if(sourceOperandMod != 0) {
                    microcodes.append("ST:\n");
                    for (String st : DataPacket.ST.get(sourceOperandMod-1)) {
                    flow.add(st);
                     microcodes.append("    ").append(st).append("\n");
                }
            }
                if(targetOperandMod != 0) {
                    microcodes.append("DT:\n");
                    for (String dt : DataPacket.DT.get(targetOperandMod-1)) {
                        flow.add(dt);
                        microcodes.append("    ").append(dt).append("\n");
                }
            }
                microcodes.append("ET:\n");
                if((sourceOperandMod == 0) && (targetOperandMod == 0)){
                    flow.add("R0+R1->R1");
                    flow.add("PC->MAR");
                    microcodes.append("    ").append("R0+R1->R1").append("\n");
                    microcodes.append("    ").append("PC->MAR").append("\n\n\n\n\n");
                }else if((sourceOperandMod == 0) && (targetOperandMod != 0)){
                    flow.add("R0+D->MDR");
                    flow.add("MDR->M");
                    flow.add("PC->MAR");
                    microcodes.append("    ").append("R0+D->MDR").append("\n");
                    microcodes.append("    ").append("MDR->M").append("\n");
                    microcodes.append("    ").append("PC->MAR").append("\n\n\n\n\n");
                }else if((sourceOperandMod != 0) && (targetOperandMod == 0)){
                    flow.add("C+R1->R1");
                    flow.add("PC->MAR");
                    microcodes.append("    ").append("C+R1->R1").append("\n");
                    microcodes.append("    ").append("PC->MAR").append("\n\n\n\n\n");
                }else{
                    flow.add("C+D->MDR");
                    flow.add("MDR->M");
                    flow.add("PC->MAR");
                    microcodes.append("    ").append("C+D->MDR").append("\n");
                    microcodes.append("    ").append("MDR->M").append("\n");
                    microcodes.append("    ").append("PC->MAR").append("\n\n\n\n\n");
                }
            }
        }
    
         execute(flow);
         
    }
  

    /*   private void debugPrint() {
            System.out.println(instructionType + "\t" + instruction + "\t" + sourceOperandMod + "\t" + targetOperandMod + "\t" + speed + "\t" + startFlag + "\t" + pauseFlag + "\t" + stepFlag);
        }
    */
    public static void main(String[] args) {
        /*test to access action.txt*/
        //  ArrayList<String> actionC = new ArrayList<String>();
        //  actionC=DataPacket.ACTION.get(DataPacket.FT.get(1));
//        FileAccess fileAccess = new FileAccess(R.file.ACTION);
//        while (fileAccess.hasNextLine()) {
        //       ArrayList<String> actionC = new ArrayList<String>();
        //       actionC=DataPacket.ACTION.get(DataPacket.FT.get(0));
                //   System.out.println(flow);
//
//            ArrayList Value = new ArrayList();
//            if (fileAccess.hasNextLine()) {
//                String[] actions = fileAccess.readLine().split(":");
//                for (String action : actions) {
//                    System.out.print(action+"::");
//                }
//
//                System.out.print("\n");
//            }
//        }


    }
}
